/* Copyright 2024 Coremail
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import fs from '@ohos.file.fs';
import type common from '@ohos.app.ability.common';
import fileuri from "@ohos.file.fileuri";
import { FileUtil } from './FileUtil';
import type { IBuffer } from 'coremail/Index';
import { getLogger } from 'coremail/Index';
import { FileBuffer } from '../basedata/FileBuffer';
import { EmlFileManager } from '../basedata/EmlFileManager';

const logger = getLogger("FileManager")
export class FileManager {

  /**
   * 查询是否文件是否已存在本地沙盒中
   * @param context ability或application的上下文的能力，用于访问沙盒
   * @param mid 文件所属的邮件id（其实应该是uid+mid）
   * @param fileName 文件名
   * @returns 存在则返回路径，不存在返回null
   */
  static async isExisted2(context:common.Context, mid:string, fileName:string, inline:boolean): Promise<string | null> {
    let filesDir = context.filesDir;
    let dir = `${filesDir}/mails/${mid}`
    if (inline) {
      dir += "/inlineattachment"
    } else {
      dir += "/attachment"
    }
    let filePath = `${dir}/${fileName}`;
    let existed = await fs.access(filePath);
    if (existed) {
      return filePath;
    }
    return null;
  }

  /**
   *
   * @param context
   * @param mid
   * @param fileName
   * @param inline
   * @returns
   */
  static async isExisted(context: common.Context,
                         mid: string,
                         fileName: string,
                         inline: boolean,
                         account: string,
                         folderName: string,
                        ): Promise<string | null> {
    let filesDir = context.filesDir;
    let relativePath = EmlFileManager.getRelativeDirPathForMailAttachment(account,mid,folderName,inline);
    let filePath = `${filesDir}/${relativePath}/${fileName}`
    let existed = await fs.access(filePath);
    if (existed) {
      return filePath;
    }
    return null;
  }


  static isFileExisted(filePath:string):boolean {
    return fs.accessSync(filePath);
  }

  /**
   * 于指定目录下创建文件夹
   * @param rootDir 父目录路径
   * @param newDirComponent 待创建文件夹的相对路径
   * @returns 新文件夹的完整路径
   */
  static createDir(rootDir:string, newDirComponent:string): string {
    let pathComponents = newDirComponent.split('/');
    let dir = `${rootDir}`
    for(let i = 0;i<pathComponents.length;i++) {
      let component = pathComponents[i];
      dir+=`/${component}`
      if(fs.accessSync(dir)){
        continue
      }
      else {
        fs.mkdirSync(dir);
      }
    }
    return dir;
  }

  /**
   * 封装通用写文件方法
   * @param data 文件内容或数据流
   * @param filePath 写入路径
   */
  static async saveData(data: IBuffer, filePath: string): Promise<void> {
    const f = new FileBuffer(filePath);
    for await (const chunk of data.readRaw()) {
      await f.feed(chunk);
    }
    await f.end();
    /*
    let file = null;
    file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    fs.writeSync(file.fd, data);
    fs.closeSync(file);
    */
  }

  /**
   * 保存文件到本地沙盒下
   * @param context ability或application的上下文的能力，用于访问沙盒
   * @param mid 文件所属的邮件id（其实应该是uid+mid）
   * @param data 文件数据流
   * @param fileName 文件名
   * @param inline 是否是内联附件
   * @returns 文件保存在沙盒中的路径
   */
  static async saveFile(
    context: common.Context,
    mid:string,
    data: IBuffer,
    rawName:string,
    fileType:string,
    inline:boolean,
    cid:string,
    account:string,
    folderFullName:string
  ): Promise<string> {
    let filesDir = context.filesDir;
    let relativeDir = EmlFileManager.getRelativeDirPathForMailAttachment(account,mid,folderFullName,inline);
    let dir = "";
    try {
      dir = this.createDir(filesDir,relativeDir);
    }
    catch(e) {
      logger.warn(e)
    }
    let fileName = EmlFileManager.getAttachNameWithRawName(cid,rawName,fileType);
    let filePath = `${dir}/${fileName}`;
    if(fs.accessSync(filePath)) {
      return filePath;
    }
    try {
      await FileManager.saveData(data, filePath);
    }
    catch(e: unknown) {
      getLogger('save-file').error("failed", e);
    }
    return filePath;
  }

  /**
   * 将沙盒路径转换为URI
   * @param filePath 沙盒路径
   * @returns URI
   */
  static uri(filePath:string): string {
    return fileuri.getUriFromPath(filePath);
  }

  /**
   * 从指定路径读取文件数据
   * @param filePath 文件路径
   * @returns 文件数据流
   */
  static async readData(filePath:string): Promise<ArrayBuffer> {
    let stat = await fs.stat(filePath) as fs.Stat;
    let buffer = new ArrayBuffer(stat.size);
    let readFile = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
    fs.readSync(readFile.fd, buffer, { offset: 0, length: stat.size });
    fs.close(readFile)
    return buffer
  }


  /**
   * 写入文件到file.
   * 写入之前,需要确保文件目录全部已经被创建.
   * 只写的方式.
   * @param content
   * @param relativePath 相对于rootDir文件夹的dirpath,不包含文件名.
   * @param rootDir,系统文件夹根目录
   * @param fileName,文件名
   */
  static writeToFile(content:string|ArrayBuffer,rootDir:string,relativePath:string,fileName:string):string {
    FileUtil.mkdirRecursivelyIfNeeded(rootDir,relativePath)
    let filePath = `${rootDir}/${relativePath}/${fileName}`
    let file = fs.openSync(filePath,fs.OpenMode.WRITE_ONLY|fs.OpenMode.CREATE);
    fs.writeSync(file.fd,content);
    fs.closeSync(file.fd);
    return filePath;
  }
}